package com.umeox.iqibla.qimi_app

import android.annotation.SuppressLint
import android.app.KeyguardManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.os.PowerManager
import android.os.RemoteException
import android.telephony.TelephonyManager
import android.text.TextUtils
import android.util.Log
import android.util.TypedValue
import android.view.MotionEvent
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageView
import androidx.lifecycle.lifecycleScope
import com.airbnb.lottie.LottieAnimationView
import com.umeox.iqibla.qimi.media.OpusDecoder
import com.umeox.iqibla.qimi.media.OpusEncoder
import com.umeox.iqibla.qimi_app.ai_image.AIExpression
import com.umeox.iqibla.qimi_app.ai_image.AbsAiImage
import com.umeox.iqibla.qimi_app.ai_image.GirlAIImage
import com.umeox.iqibla.qimi_app.media.AudioRecorder
import com.umeox.iqibla.qimi_app.media.AudioRecorderCallback
import com.umeox.iqibla.qimi_app.media.OpusStreamPlayer
import com.umeox.iqibla.qimi_app.protocol.Protocol
import com.umeox.iqibla.qimi_app.protocol.WebsocketProtocol
import com.umeox.moto.watch.themes.app.ThemeAppCompatActivity
import com.umeox.watch.moto.apiclient.model.UserAgentDTO
import com.umeox.watch.moto.dataservice.DataProvider
import com.umeox.watch.moto.dataservice.constants.Constants
import com.umeox.watch.moto.dataservice.proxy.PushApiClientProxy
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.json.JSONObject

class ChatActivity : ThemeAppCompatActivity(), WSCallback, AudioRecorderCallback {

    companion object {
        private const val TAG = "ChatActivity"
        private const val ACTION_INCOMING_CALL: String = TelephonyManager.ACTION_PHONE_STATE_CHANGED
        private const val ACTION_RINGING_VIDEO_CALL: String = Constants.ACTION_RINGING_VIDEO_CALL
        private const val ACTION_KEY_SOS: String = "com.wherecom.intent.action.ACTION_SOS"
    }

    private lateinit var ivListenState: ImageView
    private lateinit var lottieAnimationView: LottieAnimationView
    private lateinit var lottieListen: LottieAnimationView
    private lateinit var flError: FrameLayout

    /**
     * 播放组件
     */
    private val playerScope = CoroutineScope(Dispatchers.IO + Job())
    private val playerHandlerThread = HandlerThread("player").apply {
        start()
    }
    private val playerHandler = Handler(playerHandlerThread.looper)


    /**
     * 连接状态
     */
    private val connectStateFlow = MutableStateFlow(ConnectState.CONNECTING)
    private var connectState: ConnectState
        get() = connectStateFlow.value
        set(value) {
            connectStateFlow.value = value
        }

    /**
     * AI状态
     */
    private val agentStateFlow = MutableStateFlow(AgentState.HELLO)
    private var agentState: AgentState
        get() = agentStateFlow.value
        set(value) {
            agentStateFlow.value = value
        }

    /**
     * 协议实现类
     */
    private var protocol: Protocol? = null

    /**
     * 录音状态
     */
    private var isAudioRecording = false
        set(value) {
            field = value
            updateView()
        }

    /**
     * AI形态
     */
    private lateinit var aiImage: AbsAiImage

    private var wakeLock: PowerManager.WakeLock? = null
    private var kl: KeyguardManager.KeyguardLock? = null

    private val handler = Handler(Looper.getMainLooper())

    /**
     * 用于判断是否算一次交互
     */
    private var voiceSend = false

    /**
     * 倒计时
     */
    private var countdown = 60
    private val countdownRunnable = Runnable {
        countdown--
        if (countdown == 0) {
            //倒计时结束  停止录音
            stopRecord()
        } else {
            nextCountdown()
        }
    }

    private fun startRecordCountdown() {
        Log.d(TAG, "startCountdown")
        countdown = 60
        handler.postDelayed(countdownRunnable, 1000)
    }

    private fun nextCountdown() {
        Log.d(TAG, "nextCountdown: ")
        if (isAudioRecording) {
            handler.postDelayed(countdownRunnable, 1000)
        }
    }

    /**
     * 释放锁
     */
    private val releaseLockRunnable = Runnable {
        if (!isAudioRecording) {
            releaseLock()
        }
    }

    private var isCalling = false

    /**
     * 用于检测来电等
     */
    private var mCallReceiver: BroadcastReceiver = object : BroadcastReceiver() {
        //如果有电话或者视频电话进来，停止正在播放的语聊
        override fun onReceive(context: Context, intent: Intent) {
            val action = intent.action
            if (ACTION_INCOMING_CALL.equals(action, true)
                || ACTION_RINGING_VIDEO_CALL.equals(action, true)
                || ACTION_KEY_SOS.equals(action, true)
            ) {
                isCalling = true
                if (isAudioRecording) {
                    stopRecord()
                } else {
                    player?.stop()
                    //如果正在讲话 中断它
                    if (agentState == AgentState.SPEAK) {
                        lifecycleScope.launch {
                            protocol?.sendAbortSpeaking("外来语音、视频或SOS中断")
                        }
                    }
                }
            }
        }
    }

    private var agentId = ""

    /**
     * 是否常亮屏状态
     */
    private var isWakeLock = false


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_chat)
        //初始化角色
        agentId = intent?.getStringExtra("agentId") ?: BuildConfig.GRIL_AGENT_ID
        initView()
        //根据角色适配大小
        updateSize()
        //初始化形象
        aiImage = GirlAIImage()
        registerReceiver()
        initObserver()
        initState()
    }

    @SuppressLint("ClickableViewAccessibility")
    private fun initView() {
        ivListenState = findViewById(R.id.ivListenState)
        lottieAnimationView = findViewById(R.id.lottieAnimationView)
        lottieListen = findViewById(R.id.lottieListen)
        flError = findViewById(R.id.flError)
        flError.setOnClickListener {}
        ivListenState.setOnTouchListener { v, event ->
            when (event.action and MotionEvent.ACTION_MASK) {
                MotionEvent.ACTION_DOWN -> {
                    if (QiHelper.getAvailableTimes() > 0) {
                        startRecord()
                    } else {
                        val intent = Intent(this, WelcomeActivity::class.java)
                        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                        startActivity(intent)
                        finishAffinity()
                    }
                }

                MotionEvent.ACTION_UP -> {
                    if (QiHelper.getAvailableTimes() > 0) {
                        stopRecord()
                    }
                }
            }
            true
        }
    }

    private fun updateView() {
        if (isAudioRecording) {
            ivListenState.setImageResource(R.drawable.ai_function_voice_press)
        } else {
            ivListenState.setImageResource(R.drawable.ai_function_voice_default)
        }
    }


    @SuppressLint("UnspecifiedRegisterReceiverFlag")
    private fun registerReceiver() {
        val intentFilter = IntentFilter()
        intentFilter.addAction(ACTION_INCOMING_CALL)
        intentFilter.addAction(ACTION_RINGING_VIDEO_CALL)
        intentFilter.addAction(ACTION_KEY_SOS)
        registerReceiver(mCallReceiver, intentFilter)
    }

    private fun updateSize() {
        lottieAnimationView.post {
            val layoutParams = lottieAnimationView.layoutParams as FrameLayout.LayoutParams
            if (agentId == BuildConfig.BOY_AGENT_ID) {
                layoutParams.topMargin = dp2px(this, -30)
                layoutParams.height = dp2px(this, 280)
                layoutParams.width = dp2px(this, 240)
            } else {
                layoutParams.topMargin = dp2px(this, -28)
                layoutParams.height = dp2px(this, 254)
                layoutParams.width = dp2px(this, 218)
            }
            lottieAnimationView.layoutParams = layoutParams
        }
    }

    private fun dp2px(context: Context, dpValue: Int): Int {
        return TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_SP,
            dpValue.toFloat(),
            context.resources.displayMetrics
        ).toInt()
    }

    private fun showAiImage(aiExpression: AIExpression) {
        aiImage.show(lottieAnimationView, aiExpression)

    }

    private fun UserAgentDTO.effective(): Boolean {
        val urlEffective = !TextUtils.isEmpty(url)
        val tokenEffective = !TextUtils.isEmpty(token)
        val userIdEffective = !TextUtils.isEmpty(userId)
        val agentIdEffective = !TextUtils.isEmpty(agentId)
        return urlEffective && tokenEffective && userIdEffective && agentIdEffective
    }

    private fun initObserver() {
        lifecycleScope.launch {
            connectStateFlow.collectLatest {
                when (it) {
                    ConnectState.CONNECTING -> {
                        if (agentState != AgentState.HELLO) {
                            agentState = AgentState.HELLO
                        }
                    }

                    ConnectState.CONNECTED -> {
                        if (agentState != AgentState.HELLO) {
                            agentState = AgentState.HELLO
                        }
                        ivListenState.visibility = View.VISIBLE
                    }

                    ConnectState.DISCONNECTED, ConnectState.CONNECT_FAILED -> {
                        flError.visibility = View.VISIBLE
                        ivListenState.visibility = View.GONE
                        if (isAudioRecording) {
                            lottieListen.visibility = View.GONE
                            isAudioRecording = false
                            agentState = AgentState.WAIT
                            handler.removeCallbacks(countdownRunnable)
                            lifecycleScope.launch {
                                protocol?.sendStopListening()
                                recorder?.stopRecord()
                            }
                        }
                    }
                }
            }
        }
        lifecycleScope.launch {
            agentStateFlow.collectLatest {
                when (it) {
                    AgentState.HELLO -> {
                        showAiImage(AIExpression.HELLO)
                        flError.visibility = View.GONE
                    }

                    AgentState.LISTENING -> {
                        showAiImage(AIExpression.LISTENING)
                        wakeUpAndUnlock()
                    }

                    AgentState.ANALYSIS -> {
                        showAiImage(AIExpression.ANALYSIS)
                        wakeUpAndUnlock()
                    }

                    AgentState.SPEAK -> {
                        showAiImage(AIExpression.SPEAK)
                        wakeUpAndUnlock()
                    }

                    AgentState.WAIT -> {
                        showAiImage(AIExpression.LISTENING)
                        handler.postDelayed(releaseLockRunnable, 5000)
                    }
                }
            }
        }
    }

    private fun initState() {
        val appKey = QiHelper.getAppKey()
        val userId = DataProvider.getHolderId()
        lifecycleScope.launch {
            val userAgent = getUserAgent(appKey, agentId, userId)
            if (userAgent == null || !userAgent.effective()) {
                connectState = ConnectState.CONNECT_FAILED
            } else {
                connectState = ConnectState.CONNECTING
                protocol = WebsocketProtocol(
                    userAgent.url,
                    userAgent.token,
                    appKey,
                    userAgent.userId,
                    userAgent.agentId
                )
                (protocol as WebsocketProtocol?)?.setWSCallback(this@ChatActivity)
                protocol?.start()
                if (protocol?.openAudioChannel() == true) {
                    Log.d(TAG, "webSocket初始化成功")
                    initPlayer()
                    delay(1000)
                    initSender()
                } else {
                    Log.d(TAG, "webSocket初始化失败")
                    connectState = ConnectState.CONNECT_FAILED
                }
            }
        }
    }

    private var player: OpusStreamPlayer? = null
    private var decoder: OpusDecoder? = null
    private fun initPlayer() {
        playerScope.launch {
            val sampleRate = 24000
            val channels = 1
            val frameSizeMs = 60
            player = OpusStreamPlayer(sampleRate, channels, frameSizeMs)
            decoder = OpusDecoder(sampleRate, channels, frameSizeMs)
            player?.start()
        }
    }

    /**
     * 录制器
     */
    private var encoder: OpusEncoder? = null
    private var recorder: AudioRecorder? = null
    private fun initSender() {
        val sampleRate = 16000
        val channels = 1
        val frameSizeMs = 60
        encoder = OpusEncoder(sampleRate, channels, frameSizeMs)
        recorder = AudioRecorder(sampleRate, channels, frameSizeMs)
        recorder?.setAudioRecorderCallback(this)
        recorder?.initRecord()
    }

    @Throws(RemoteException::class)
    fun getUserAgent(appKey: String, agentId: String, userId: String): UserAgentDTO? {
        return try {
            PushApiClientProxy.fetchBinder().getAgentUser(appKey, agentId, userId)
        } catch (e: Exception) {
            e.printStackTrace()
            return null
        }
    }

    override fun onConnecting() {
        connectState = ConnectState.CONNECTING
    }

    override fun onConnected() {
        connectState = ConnectState.CONNECTED
    }

    override fun onDisConnected() {
        connectState = ConnectState.DISCONNECTED
    }

    override fun onConnectFailed(reason: String) {
        connectState = ConnectState.DISCONNECTED
    }

    private fun startRecord() {
        isCalling = false
        lottieListen.visibility = View.VISIBLE
        handler.removeCallbacks(releaseLockRunnable)
        isAudioRecording = true
        agentState = AgentState.LISTENING
        player?.stop()
        startRecordCountdown()
        voiceSend = false
        lifecycleScope.launch {
            protocol?.sendStartListening()
            recorder?.startRecord()
        }
    }

    private fun stopRecord() {
        lottieListen.visibility = View.GONE
        isAudioRecording = false
        agentState = AgentState.ANALYSIS
        handler.removeCallbacks(countdownRunnable)
        lifecycleScope.launch {
            protocol?.sendStopListening()
            recorder?.stopRecord()
        }
        if (voiceSend) {
            QiHelper.usageStatistics(agentId)
        } else {
            agentState = AgentState.WAIT
        }
    }

    override fun onDestroy() {
        releaseLock()
        super.onDestroy()
        protocol?.dispose()
        encoder?.release()
        decoder?.release()
        player?.stop()
        unregisterReceiver(mCallReceiver)
        recorder?.release()
    }


    override fun onMessageIncoming(jsonObject: JSONObject) {
        Log.d(TAG, "$jsonObject")
        val type = jsonObject.optString("type")
        if (!TextUtils.isEmpty(type)) {
            val state = jsonObject.optString("state")
            if ("tts" == type && "stop" == state && agentState == AgentState.SPEAK) {
                Log.d(TAG, "说话结束 5秒后息屏")
                handler.postDelayed(releaseLockRunnable, 5000)
                agentState = AgentState.WAIT
            } else if ("tts" == type && "sentence_start" == state) {
                if (isAudioRecording) {
                    Log.d(TAG, "再次录音中，不转换为说话状态")
                    return
                }
                if (agentState != AgentState.SPEAK) {
                    agentState = AgentState.SPEAK
                }
            }
        }
    }

    override fun onAudioIncoming(byteArray: ByteArray) {
        if (isAudioRecording) {
            Log.d(TAG, "录音中，停止接收语音流")
            return
        }
        if (isCalling) {
            Log.d(TAG, "来电中，停止接收语音流")
            return
        }
        playerHandler.post {
            player?.play(decoder?.decode(byteArray))
        }
    }

    override fun audioRecord(byteArray: ByteArray) {
        lifecycleScope.launch(Dispatchers.IO) {
            if (encoder == null) {
                Log.d(TAG, "audioRecord: encoder为空")
            } else {
                val result = encoder!!.encode(byteArray)
                if (result != null) {
                    voiceSend = true
                    protocol?.sendAudio(result)
                } else {
                    Log.d(TAG, "audioRecord: result为空")
                }
            }
        }
    }

    @SuppressLint("WakelockTimeout")
    private fun wakeUpAndUnlock() {
        Log.d(TAG, "wakeUpAndUnlock isWakeLock = $isWakeLock")
        if (isWakeLock) {
            return
        }
        isWakeLock = true
        if (kl == null) {
            val km = getSystemService(KEYGUARD_SERVICE) as KeyguardManager
            kl = km.newKeyguardLock("unLock")
        }
        //解锁
        kl?.disableKeyguard()

        if (wakeLock == null) {
            val pm = getSystemService(POWER_SERVICE) as PowerManager
            wakeLock = pm.newWakeLock(
                PowerManager.ACQUIRE_CAUSES_WAKEUP or PowerManager.FULL_WAKE_LOCK,
                javaClass.simpleName
            )
        }
        // 点亮亮屏
        wakeLock?.acquire()
    }

    private fun releaseLock() {
        Log.d(TAG, "releaseLock isWakeLock = $isWakeLock")
        if (!isWakeLock) {
            return
        }
        isWakeLock = false
        if (wakeLock != null) {
            Log.d(TAG, "Releasing wakelock")
            try {
                wakeLock?.release()
            } catch (th: Throwable) {
                Log.d(TAG, "Releasing wakelock failed:$th")
            }
        } else {
            Log.d(TAG, "Wakelock reference is null")
        }
    }

}